package cryptosuite

import (
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/core"
	"github.com/pkg/errors"
)

// CryptoConfigOptions 加密配置选择项
type CryptoConfigOptions struct {
	isSecurityEnabled
	securityAlgorithm
	securityLevel
	securityProvider
	keyStorePath
}

type applier func()
type predicate func() bool
type setter struct{ isSet bool }

type isSecurityEnabled interface {
	IsSecurityEnabled() bool
}

type securityAlgorithm interface {
	SecurityAlgorithm() string
}

type securityLevel interface {
	SecurityLevel() int
}

type securityProvider interface {
	SecurityProvider() string
}

type keyStorePath interface {
	KeyStorePath() string
}

func BuildCryptoSuiteConfigFromOptions(opts ...interface{}) (core.CryptoSuiteConfig, error) {
	c := &CryptoConfigOptions{}
	for _, option := range opts {
		err := setCryptoConfigWithOptionInterface(c, option)
		if err != nil {
			return nil, err
		}
	}

	return c, nil

}

func UpdateMissingOptsWithDefaultConfig(c *CryptoConfigOptions, d core.CryptoSuiteConfig) core.CryptoSuiteConfig {
	s := &setter{}

	s.set(c.isSecurityEnabled, nil, func() { c.isSecurityEnabled = d })
	s.set(c.securityAlgorithm, nil, func() { c.securityAlgorithm = d })
	s.set(c.securityLevel, nil, func() { c.securityLevel = d })
	s.set(c.securityProvider, nil, func() { c.securityProvider = d })
	s.set(c.keyStorePath, nil, func() { c.keyStorePath = d })

	return c
}

func IsCryptoConfigFullyOverridden(c *CryptoConfigOptions) bool {
	return !anyNil(c.isSecurityEnabled, c.securityAlgorithm, c.securityLevel, c.securityProvider, c.keyStorePath)
}

func setCryptoConfigWithOptionInterface(c *CryptoConfigOptions, o interface{}) error {
	s := &setter{}

	s.set(c.isSecurityEnabled, func() bool { _, ok := o.(isSecurityEnabled); return ok }, func() { c.isSecurityEnabled = o.(isSecurityEnabled) })
	s.set(c.securityAlgorithm, func() bool { _, ok := o.(securityAlgorithm); return ok }, func() { c.securityAlgorithm = o.(securityAlgorithm) })
	s.set(c.securityLevel, func() bool { _, ok := o.(securityLevel); return ok }, func() { c.securityLevel = o.(securityLevel) })
	s.set(c.securityProvider, func() bool { _, ok := o.(securityProvider); return ok }, func() { c.securityProvider = o.(securityProvider) })
	s.set(c.keyStorePath, func() bool { _, ok := o.(keyStorePath); return ok }, func() { c.keyStorePath = o.(keyStorePath) })

	if !s.isSet {
		return errors.Errorf("option %#v is not a sub interface of CryptoSuiteConfig, at least one of its functions must be implemented.", o)
	}
	return nil
}

func (o *setter) set(current interface{}, check predicate, apply applier) {
	if current == nil && (check == nil || check()) {
		apply()
		o.isSet = true
	}
}

func anyNil(objs ...interface{}) bool {
	for _, p := range objs {
		if p == nil {
			return true
		}
	}
	return false
}
